home *** CD-ROM | disk | FTP | other *** search
/ Disc to the Future 2 / Disc to the Future Part II Programmer's Reference (Wayzata Technology)(6013)(1992).bin / MAC / THINKC / 3_0 / MACBINAR / MBGET.C next >
C/C++ Source or Header  |  1988-06-14  |  9KB  |  539 lines

  1. /*
  2. Date: Tue, 12 Apr 88 22:11:44 EDT
  3. From: singer@harvard.harvard.edu (Jon Hueras)
  4. Subject: mbget.c
  5.  
  6. The comments below notwithstanding, this program does not do
  7. Macterminal 1.1 style transfers, but rather MacBinary style
  8. transfers. It still uses XModem transport protocol, but
  9. this version has been modified to recognize and use CRCs
  10. instead of 8-bit checksums when appropriate.
  11.  
  12. This program is supplied as-is, which is to say that this
  13. is what I have been using for the past two years, ever
  14. since I stopped using MacTerminal and started using Red
  15. Ryder. I have used it successfully with Red Ryder versions
  16. 9.2 through 10.2 inclusive, and possibly even earlier versions
  17. than that. I have no knowledge of its performance with any
  18. other terminal emulation software.
  19.  
  20.     Jon Hueras
  21.     Symantec/THINK Technologies
  22.     singer@endor.harvard.edu
  23.  
  24. */
  25. #include <stdio.h>
  26. #include <signal.h>
  27. #include <setjmp.h>
  28. #include <sgtty.h>
  29.  
  30. #ifdef NO_RENAME
  31. #define rename(old, new)    link(old, new); unlink(old)
  32. #endif
  33.  
  34. #define RECORDBYTES 132
  35. #define DATABYTES 128
  36. #define NAMEBYTES 63
  37.  
  38. #define RETRIES 10
  39. #define SOHTIMO 10
  40. #define LINTIMO 20
  41. #define CHRTIMO 2
  42.  
  43. #define MAXRECNO 0xff
  44. #define BYTEMASK 0xff
  45.  
  46. #define TMO -1
  47. #define DUP '\000'
  48. #define SOH '\001'
  49. #define EOT '\004'
  50. #define ACK '\006'
  51. #define NAK '\025'
  52. #define CAN '\030'
  53. #define EEF '\032'
  54. #define ESC '\033'
  55.  
  56. #define H_NLENOFF 1
  57. #define H_NAMEOFF 2
  58. /* 65 <-> 80 is the FInfo structure */
  59. #define H_TYPEOFF 65
  60. #define H_AUTHOFF 69
  61.  
  62. #define H_LOCKOFF 81
  63. #define H_DLENOFF 83
  64. #define H_RLENOFF 87
  65. #define H_CTIMOFF 91
  66. #define H_MTIMOFF 95
  67.  
  68. #define H_OLD_DLENOFF 81
  69. #define H_OLD_RLENOFF 85
  70.  
  71. #define TEXT 0
  72. #define DATA 1
  73. #define RSRC 2
  74. #define FULL 3
  75.  
  76. int mode, txtmode;
  77.  
  78. struct macheader {
  79.     char m_name[NAMEBYTES+1];
  80.     char m_type[4];
  81.     char m_author[4];
  82.     long m_datalen;
  83.     long m_rsrclen;
  84.     long m_createtime;
  85.     long m_modifytime;
  86. } mh;
  87.  
  88. struct filenames {
  89.     char f_info[256];
  90.     char f_data[256];
  91.     char f_rsrc[256];
  92. } files;
  93.  
  94. char tmpname[16];
  95.  
  96. int lastack, crc;
  97. char buf[DATABYTES];
  98.  
  99. /*
  100.  * macget -- receive file from macintosh using xmodem protocol
  101.  * Dave Johnson, Brown University Computer Science
  102.  *
  103.  * (c) 1984 Brown University 
  104.  * may be used but not sold without permission
  105.  *
  106.  * created ddj 5/22/84 
  107.  * revised ddj 6/29/84 -- added [-rdu] options
  108.  * revised ddj 7/16/84 -- protocol changes for MacTerminal Beta Version 0.5X
  109.  * revised ddj 7/31/84 -- pre-4.2 signal bugs fixed in timedout()
  110.  * revised ddj 11/7/84 -- renamed send_sync() -> get_sync()
  111.  * revised jfh 6/24/86 -- modified to do MacBinary transfers, rather than
  112.  *                        MacTerminal's mac-to-mac style transfers, and
  113.  *                        added CRC handling to the XModem layer. Also
  114.  *                        enabled the <ESC>B auto-receive feature. Changed
  115.  *                        name to mbget ('mb' for MacBinary) to avoid
  116.  *                        confusion with the original macget.
  117.  */
  118. char usage[] = "usage: \"mbget [-rdu] [filename]\"\n";
  119.  
  120. main(ac, av)
  121. char **av;
  122. {
  123.     char *name;
  124.  
  125.     mode = FULL;
  126.     name = "";
  127.     ac--; av++;
  128.     while (ac) {
  129.         if (av[0][0] == '-') {
  130.             switch (av[0][1]) {
  131.             case 'r':
  132.                 mode = RSRC;
  133.                 break;
  134.             case 'd':
  135.                 mode = DATA;
  136.                 break;
  137.             case 'u':
  138.                 mode = TEXT;
  139.                 break;
  140.             default:
  141.                 fprintf(stderr, usage);
  142.                 exit(1);
  143.             }
  144.         }
  145.         else {
  146.             name = av[0];
  147.         }
  148.         ac--; av++;
  149.     }
  150.  
  151.     setup_tty();
  152.     if (get_sync()) {
  153.         lastack = 0;
  154.         txtmode = 0;
  155.         recv_hdr(name);
  156.         if (mode == TEXT) txtmode++;
  157.         recv_file(files.f_data, mh.m_datalen, 1);
  158.         txtmode = 0;
  159.         recv_file(files.f_rsrc, mh.m_rsrclen, 0);
  160.     }
  161.     reset_tty();
  162. }
  163.  
  164. recv_hdr(name)
  165. char *name;
  166. {
  167.     long get4();
  168.     int n;
  169.     FILE *fp;
  170.     char *np;
  171.  
  172.     strcpy(tmpname, "#machdrXXXXXX");
  173.     mktemp(tmpname);
  174.     recv_file(tmpname, (long)DATABYTES, 1);
  175.  
  176.     fp = fopen(tmpname, "r");
  177.     if (fp == NULL) {
  178.         perror("temp file");
  179.         cleanup(-1);
  180.     }
  181.     fread(buf, 1, DATABYTES, fp);
  182.     fclose(fp);
  183.  
  184.     if (name && *name) {
  185.         n = strlen(name);
  186.         if (n > NAMEBYTES) n = NAMEBYTES;
  187.         strncpy(mh.m_name, name, n);
  188.         mh.m_name[n] = '\0';
  189.     }
  190.     else {
  191.         n = buf[H_NLENOFF] & BYTEMASK;
  192.         if (n > NAMEBYTES) n = NAMEBYTES;
  193.         strncpy(mh.m_name, buf + H_NAMEOFF, n);
  194.         mh.m_name[n] = '\0';
  195.     }
  196.     for (np = mh.m_name; *np; np++)
  197.         if (*np == ' ') *np = '_';
  198.  
  199.     if (mode == FULL) {
  200.         sprintf(files.f_info, "%s.info", mh.m_name);
  201.         rename(tmpname, files.f_info);
  202.         tmpname[0] = '\0';
  203.         sprintf(files.f_data, "%s.data", mh.m_name);
  204.         sprintf(files.f_rsrc, "%s.rsrc", mh.m_name);
  205.     }
  206.     else {
  207.         unlink(tmpname);
  208.         tmpname[0] = '\0';
  209.         switch (mode) {
  210.         case RSRC:
  211.             sprintf(files.f_data, "/dev/null");
  212.             sprintf(files.f_rsrc, "%s.rsrc", mh.m_name);
  213.             break;
  214.  
  215.         case DATA:
  216.             sprintf(files.f_data, "%s.data", mh.m_name);
  217.             sprintf(files.f_rsrc, "/dev/null");
  218.             break;
  219.  
  220.         case TEXT:
  221.             sprintf(files.f_data, "%s", mh.m_name);
  222.             sprintf(files.f_rsrc, "/dev/null");
  223.             break;
  224.         }
  225.     }
  226.  
  227.     strncpy(mh.m_type, buf + H_TYPEOFF, 4);
  228.     strncpy(mh.m_author, buf + H_AUTHOFF, 4);
  229.     mh.m_datalen = get4(buf + H_DLENOFF);
  230.     mh.m_rsrclen = get4(buf + H_RLENOFF);
  231.     mh.m_createtime = get4(buf + H_CTIMOFF);
  232.     mh.m_modifytime = get4(buf + H_MTIMOFF);
  233. }
  234.  
  235. recv_file(fname, bytes, more)
  236. char *fname;
  237. long bytes;
  238. int more;
  239. {
  240.     register int status, n;
  241.     FILE *outf;
  242.     int naks = 0;
  243.  
  244.     outf = fopen(fname, "w");
  245.     if (outf == NULL) {
  246.         perror(fname);
  247.         cleanup(-1);
  248.     }
  249.     for (;;) {
  250.         if (!bytes) {
  251.             if (!more)
  252.                 tputc(ACK);
  253.             fclose(outf);
  254.             return;
  255.         }
  256.         status = rec_read(buf, DATABYTES);
  257.         switch (status) {
  258.         case EOT:
  259.             tputc(ACK);
  260.             fclose(outf);
  261.             if (more) {
  262.                 purge(SOHTIMO);
  263.                 cleanup(-1);
  264.             }
  265.             return;
  266.         case ACK:
  267.             tputc(ACK);
  268.             naks = 0;
  269.             n = (bytes > DATABYTES) ? DATABYTES : bytes;
  270.             bytes -= n;
  271.             fwrite(buf, n, 1, outf);
  272.             break;
  273.         case DUP:
  274.             tputc(ACK);
  275.             naks = 0;
  276.             break;
  277.         case NAK:
  278.             purge(CHRTIMO);
  279.             if (naks++ < RETRIES) {
  280.                 tputc(NAK);
  281.                 break;
  282.             }
  283.             /* fall through */
  284.         case CAN:
  285.             tputc(CAN);
  286.             fclose(outf);
  287.             /* unlink fname? */
  288.             cleanup(-1);
  289.             /* NOTREACHED */
  290.         }
  291.     }
  292. }
  293.  
  294. get_sync()
  295. {
  296.     int c, i;
  297.     
  298.     for (;;) {
  299.         if ((c = tgetc(15)) == TMO)
  300.             break;
  301.         if (c != ESC)
  302.             continue;
  303.         if ((c = tgetc(1)) == TMO)
  304.             continue;
  305.         if (c == 'b')
  306.             break;
  307.     }
  308.     
  309.     for (i = 0; i < 3; i++) {
  310.         tputc('C');
  311.         if ((c = tgetc(SOHTIMO)) != SOH)
  312.             continue;
  313.         tungetc(c);
  314.         crc++;
  315.         return 1;
  316.     }
  317.     
  318.     for (i = 0; i < 3; i++) {
  319.         tputc(NAK);
  320.         if ((c = tgetc(SOHTIMO)) != SOH)
  321.             continue;
  322.         tungetc(c);
  323.         return 1;
  324.     }
  325.     
  326.     tputc(CAN);
  327.     return 0;
  328. }
  329.  
  330. rec_read(buf, recsize)
  331. char buf[];
  332. int recsize;
  333. {
  334.     int c, rec, rec_bar, cksum;
  335.     long tgetrec();
  336.  
  337.     c = tgetc(SOHTIMO);
  338.     switch (c) {
  339.     case TMO:
  340.     default:
  341.         return NAK;
  342.     case EOT:
  343.         return EOT;
  344.     case CAN:
  345.         return CAN;
  346.     case SOH:
  347.         /* read header */
  348.         rec = tgetc(CHRTIMO);
  349.         if (rec == TMO)
  350.             return NAK;
  351.         rec_bar = tgetc(CHRTIMO);
  352.         if (rec_bar == TMO)
  353.             return NAK;
  354.  
  355.         /* check header */
  356.         if (rec != MAXRECNO - rec_bar) return NAK;
  357.  
  358.         /* fill buffer */
  359.         if ((cksum = tgetrec(buf, recsize, LINTIMO)) == TMO)
  360.             return NAK;
  361.  
  362.         /* get checksum */
  363.         c = tgetc(CHRTIMO);
  364.         if (c == TMO)
  365.             return NAK;
  366.         if (crc) {
  367.             if (c != ((cksum >> 8) & BYTEMASK))
  368.                 return NAK;
  369.             c = tgetc(CHRTIMO);
  370.             if (c == TMO)
  371.                 return NAK;
  372.         }
  373.         if (c != (cksum & BYTEMASK))
  374.             return NAK;
  375.  
  376.         /* check record number */
  377.         if (rec == lastack)
  378.             return DUP;
  379.         if (rec != ((lastack + 1) & MAXRECNO))
  380.             return CAN;
  381.         else {
  382.             lastack = rec;
  383.             return ACK;
  384.         }
  385.     }
  386.     /* NOTREACHED */
  387. }
  388.  
  389. purge(timeout)
  390. int timeout;
  391. {
  392.     int c;
  393.  
  394.     do {
  395.         c = tgetc(timeout);
  396.     } while (c != TMO);
  397. }
  398.  
  399. static int ttyfd;
  400. static FILE *ttyf;
  401. jmp_buf timobuf;
  402.  
  403. long tgetrec(buf, count, timeout)
  404. char *buf;
  405. int count, timeout;
  406. {
  407.     char *bp;
  408.     int i, cksum;
  409.  
  410.     if (setjmp(timobuf))
  411.         return TMO;
  412.     
  413.     alarm(timeout);
  414.     i = fread(buf, 1, count, ttyf);
  415.     alarm(0);
  416.     if (i != count)
  417.         return TMO;
  418.     
  419.     if (crc)
  420.         cksum = calcrc(buf, count);
  421.     
  422.     if (!crc)
  423.         cksum = 0;
  424.     bp = buf;
  425.     for (i = 0; i < count; bp++, i++) {
  426.         if (!crc)
  427.             cksum += *bp;
  428.         if (txtmode && *bp == '\r')
  429.             *bp = '\n';
  430.     }
  431.     
  432.     return cksum & 0xFFFF;
  433. }
  434.  
  435. tgetc(timeout)
  436. int timeout;
  437. {
  438.     int c;
  439.  
  440.     if (setjmp(timobuf))
  441.         return TMO;
  442.  
  443.     alarm(timeout);
  444.     c = getc(ttyf);
  445.     alarm(0);
  446.  
  447.     if (c == -1)    /* probably hung up or logged off */
  448.         return EOT;
  449.     else
  450.         return c & BYTEMASK;
  451. }
  452.  
  453. tungetc(c)
  454. char c;
  455. {
  456.     ungetc(c, ttyf);
  457. }
  458.  
  459. tputc(c)
  460. char c;
  461. {
  462.     write(ttyfd, &c, 1);
  463. }
  464.  
  465. timedout()
  466. {
  467.     signal(SIGALRM, timedout);    /* for pre-4.2 systems */
  468.     longjmp(timobuf, 1);
  469. }
  470.  
  471. static struct sgttyb otty, ntty;
  472. /* should turn messages off */
  473.  
  474. setup_tty()
  475. {
  476.     int cleanup();
  477.     int timedout();
  478.  
  479.     ttyf = stdin;
  480.     ttyfd = fileno(stdout);
  481.     ioctl(ttyfd, TIOCGETP, &otty);
  482.     signal(SIGHUP, cleanup);
  483.     signal(SIGINT, cleanup);
  484.     signal(SIGQUIT, cleanup);
  485.     signal(SIGTERM, cleanup);
  486.     signal(SIGALRM, timedout);
  487.     ntty = otty;
  488.     ntty.sg_flags = RAW|ANYP;
  489.     ioctl(ttyfd, TIOCSETP, &ntty);
  490. }
  491.  
  492. reset_tty()
  493. {
  494.     sleep(5);    /* should wait for output to drain */
  495.     ioctl(ttyfd, TIOCSETP, &otty);
  496. }
  497.  
  498. cleanup(sig)
  499. int sig;
  500. {
  501.     if (tmpname[0] != '\0')
  502.         unlink(tmpname);
  503.     reset_tty();
  504.     exit(sig);
  505. }
  506.  
  507. long
  508. get4(bp)
  509. char *bp;
  510. {
  511.     register int i;
  512.     long value = 0;
  513.  
  514.     for (i = 0; i < 4; i++) {
  515.         value <<= 8;
  516.         value |= (*bp & BYTEMASK);
  517.         bp++;
  518.     }
  519.     return value;
  520. }
  521.  
  522. int calcrc(ptr,    count)
  523. char *ptr;
  524. int count;
  525.     {
  526.         int    crc, i;
  527.  
  528.         crc    = 0;
  529.         while (--count >= 0) {
  530.          crc ^= ((int) *ptr++) << 8;
  531.          for (i = 0; i < 8; ++i)
  532.                  if (crc & 0x8000)
  533.              crc = crc <<    1 ^ 0x1021;
  534.                  else
  535.              crc <<= 1;
  536.          }
  537.         return (crc    & 0xFFFF);
  538.     }
  539.